package jacktgq.moveTheBox;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;

/**
 * @Author CandyWall
 * @Date 2021/3/28--23:04
 * @Description
 */
public class GameData {
    public static final BufferedImage[] BOX_IMAGES;

    static {
        BOX_IMAGES = new BufferedImage[10];
        for (int i = 0; i < 10; i++) {
            BOX_IMAGES[i] = readImage(i + "");
        }
    }

    private static BufferedImage readImage(String filename) {
        try {
            return ImageIO.read(GameData.class.getResource("/jacktgq/moveTheBox/resources/" + filename + ".png"));
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("读取图片资源失败！");
        }
    }

    private int maxTurn;
    private Board starterBoard;
    private int M, N;
    private Board showBoard;

    public static final int[][] dirs = {{1, 0}, {0, 1}, {-1, 0}};
    private boolean[][] visited;

    public GameData(String filename) {
        if (filename == null) {
            throw new IllegalArgumentException("文件名不能为空");
        }

        File file = new File(filename);
        if (!file.exists()) {
            throw new IllegalArgumentException("文件不存在");
        }
        try (Scanner scanner = new Scanner(new FileReader(file))) {
            this.maxTurn = Integer.parseInt(scanner.nextLine());

            if (maxTurn < 0) {
                throw new IllegalArgumentException("游戏可操作步数不能小于0！");
            }

            ArrayList<String> lines = new ArrayList<>();
            while (scanner.hasNextLine()) {
                lines.add(scanner.nextLine());
            }

            starterBoard = new Board(lines.toArray(new String[0]));
            System.out.println(starterBoard);
            M = starterBoard.getM();
            N = starterBoard.getN();

            showBoard = new Board(starterBoard);

            visited = new boolean[M][N];
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public int getM() {
        return M;
    }

    public int getN() {
        return N;
    }

    public Board getShowBoard() {
        return showBoard;
    }

    public static void main(String[] args) {
        new GameData("data/moveTheBox.txt");
    }

    public boolean solve() {
        return solve(starterBoard, maxTurn);
    }

    // 通过盘面board，使用turn次move，解决move the box问题
    // 若可以成功解决，则会返回true，否则返回false
    private boolean solve(Board board, int turn) {
        if (board == null || turn < 0) {
            throw new IllegalArgumentException("盘面不能为空，并且可移动次数不能小于0！");
        }

        // 如果已经没有移动步数了，先判断当前盘面是否胜利
        if (turn == 0) {
            return board.isWin();
        }

        // 如果还有移动步数，但是已经达成了游戏胜利状态，可以直接退出，不必继续移动
        if (board.isWin()) {
            return true;
        }

        // 遍历盘面
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                // 如果某个位置不是箱子，就可以尝试去挪动它
                if (board.getDataAt(i, j) != Board.EMPTY) {
                    // 尝试向箱子的四周挪动或者交换
                    for (int k = 0; k < dirs.length; k++) {
                        int nextI = i + dirs[k][0];
                        int nextJ = j + dirs[k][1];
                        // 待挪到的位置没有出界并且没有被遍历过
                        if (board.inArea(nextI, nextJ) && !visited[nextI][nextJ]) {
                            String swapString = String.format("交换 (%d, %d) 和 (%d, %d)", i, j, nextI, nextJ);
                            Board nextBoard = new Board(board, board, swapString);
                            // 交换两个元素的位置
                            nextBoard.swap(i, j, nextI, nextJ);
                            // 尝试运行盘面，执行消除操作
                            nextBoard.run();
                            if(solve(nextBoard, turn - 1)) {
                                return true;
                            }
                        }
                    }
                }
            }
        }

        return false;
    }

}
